﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using FastReflectionLib;
using System.Extensions;

namespace HPPClientLibrary
{
    /// <summary>
    /// 实现 INotifyPropertyChanged接口实现类 与 某个类的Property 绑定
    /// </summary>
    public class BindProxy
    {
        private static readonly Dictionary<BindKey, Dictionary<BindKey, Func<object, bool>>> Dict = new Dictionary<BindKey, Dictionary<BindKey, Func<object, bool>>>();

        

        /// <summary>
        /// 添加绑定
        /// </summary>
        /// <param name="target">监听对象</param>
        /// <param name="propertyName">监听对象的PropertyName</param>
        /// <param name="instance">被监听的对象</param>
        /// <param name="memberName">被监听的PropertyName</param>
        /// <param name="validFunc">验证委托</param>
        public static void AddBind(object target, string propertyName, INotifyPropertyChanged instance, string memberName, Func<object, bool> validFunc)
        {
            if (instance == null || target == null || string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(memberName))
            {
                return;
            }

            var notifier = new BindKey { InvokeObject = instance, MemberName = memberName };

            var observer = new BindKey { InvokeObject = target, MemberName = propertyName };

            lock (Dict)
            {
                if (!Dict.ContainsKey(notifier))
                {
                    Dict.Add(notifier, new Dictionary<BindKey, Func<object, bool>>());
                    instance.PropertyChanged += InstancePropertyChanged;
                }

                var bindKeyFuncDict = Dict[notifier];
                if (!bindKeyFuncDict.ContainsKey(observer))
                {
                    bindKeyFuncDict.Add(observer, validFunc);
                }
            }



        }

        static void InstancePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var notifier = new BindKey() { InvokeObject = sender, MemberName = e.PropertyName };
            if (Dict.ContainsKey(notifier))
            {
                var hashSet = Dict[notifier];
                foreach (KeyValuePair<BindKey, Func<object, bool>> pair in hashSet)
                {
                    var notifyPropertyInfo = sender.GetType().GetProperty(e.PropertyName);

                    
                    
                    object value = notifyPropertyInfo.FastGetValue(sender);

                    if (pair.Value != null && pair.Value(value) || pair.Value == null)
                    {
                        var targetPropertyInfo = pair.Key.InvokeObject.GetType().GetProperty(pair.Key.MemberName);
                        targetPropertyInfo.FastSetValue(pair.Key.InvokeObject, value);
                    }

                }
            }
        }

        /// <summary>
        /// 表示一个对象与其一个Property名字
        /// </summary>
        private class BindKey
        {
            private object _invokeObject;
            private string _memberName;

            public string MemberName
            {
                get { return _memberName; }
                set { _memberName = value; }
            }

            public object InvokeObject
            {
                get { return _invokeObject; }
                set { _invokeObject = value; }
            }

            public override bool Equals(object obj)
            {
                if (obj == null) return false;
                if (object.ReferenceEquals(this, obj)) return true;
                //
                if (this.GetType() != obj.GetType()) return false;
                //
                return CompareMembers(obj as BindKey);
            }

            private bool CompareMembers(BindKey other)
            {
                return _invokeObject.Equals(other._invokeObject) && _memberName.Equals(other._memberName);
            }

            public override int GetHashCode()
            {
                return _invokeObject.GetHashCode() + _memberName.GetHashCode();
            }
        }
    }
}